home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
130 MIDI Tool Box
/
130 MIDI Tool Box.iso
/
sysex
/
umpu.pas
< prev
Wrap
Pascal/Delphi Source File
|
1991-04-08
|
7KB
|
256 lines
Unit Umpu;
{
****************************************************************************
This Unit is derived from an article written in the May 1989 issue of
Electronic Musician Magazine entitled 'Handling MPU-401 Interrupts with
Turbo Pascal' by William Millar. The Code was coded into Unit form by
John Sloan CIS. 71310,2267.
****************************************************************************
}
Interface
Const
MPU_reset = $ff;
MPU_UART_Mode = $3f;
var Systemtick:longint absolute $40:$6c;
MPU_in_UART_mode : Boolean;
Procedure Disable_CPU_Interrupts;
INLINE($FA);{CLI}
Procedure Enable_CPU_Interrupts;
INLINE($FB); {STI}
Procedure Disable_MPU_Interrupts;
INLINE($FA/$BA/$21/$00/$EC/$0C/$04/$EE/$FB);
Procedure Enable_MPU_Interrupts;
INLINE($FA/$BA/$21/$00/$EC/$24/$FB/$EE/$FB);
Procedure Ack_Int_to_PIC;
INLINE($BA/$20/$00/$B0/$20/$EE);
Function Get_Data_From_MPU(Var data:Byte):Boolean;
Function Get_Data_With_Timeout(Var data:Byte;
Expected:integer;
timeout:word):Boolean;
Procedure Send_Command_to_MPU(Command:Byte);
Procedure Send_Data_to_MPU(Data:Byte);
Procedure ClearMpuIn;
Procedure Mpu_Init;
Implementation
Uses Dos;
const
MPU_ack =$fe;
MPU_Dataport =$330;
MPU_Statport =$331;
MPU_Comport =$331;
MPU_Data_Available_Mask = $80;
MPU_Data_Ready_Mask = $40;
MPU_Interrupt_Number = 10;
Buffer_size =4095; {keep power of 2}
Buffer_start =0;
Buffer_End =Buffer_size;
Type
Data_Buffer = Array[Buffer_Start..Buffer_End] of Byte;
Var
MPU_Indata_Buffer : Data_Buffer;
Buffer_Head,
Buffer_Tail : Word;
Old_Int_Vec : Pointer;
ExitSave : Pointer;
Procedure MPU_Interrupt_Handler;
{***************************************************************************
This routine is called every time the hardware IRQ2 toggles as a result of
data being received by the MPU-401. The hardware vector to this routine is
entered into the PC's interrupt vector table in the initialization section
of this Unit. The pseudocode is something like:
Disable any further interrupts from bothering us
If the status port data available bit is reset then
Write the data to the next location in the buffer
If the pointer to this data location hasn't reached the
end of the buffer then increment it, otherwise make
it point to the beginning of the buffer
Enable the CPU interrupt system
Acknowledge to the PIC chip that we responded to this IRQ
*****************************************************************************
}
Interrupt;
Begin
Disable_MPU_Interrupts;
While((Port[MPU_statport] AND MPU_Data_Available_Mask)=0) DO
Begin
MPU_Indata_BUffer[Buffer_Head]:=Port[MPU_DataPort];
buffer_head:=(buffer_head+1) and Buffer_size;
End;
Enable_MPU_Interrupts;
ack_Int_to_PIC;
End;
Function Get_Data_From_MPU;
{
****************************************************************************
Disable CPU interrupts
If the buffer beginning pointer and the end pointer are different then we
must have data therefore:
Renable interrupts so we don't lose any data
Get the data from the buffer
If the pointer to this data isn't at the end of buffer increment it
otherwise reset it to point to the buffer's beginning
If data was available, pass the data back to the calling routine
as well as a boolean true (data available) otherwise
pass a boolean false (data not available)
*****************************************************************************
}
Var
Valid:boolean;
Begin
Disable_MPU_Interrupts;
Valid:=(Buffer_Head <> Buffer_Tail);
Enable_MPU_Interrupts;
If (Valid) then
begin
Data:=MPU_Indata_Buffer[buffer_Tail];
buffer_tail:=(buffer_tail+1) and Buffer_size;
end;
Get_data_From_MPU:=valid
end;{Get..}
{wait for data, ignore clock, timeout}
Function Get_Data_With_Timeout(Var data:Byte;
Expected:integer;
timeout:word):Boolean;
var t:longint;
begin
Get_data_with_timeout:=false;
t:=systemtick;
while not (Get_data_from_mpu(data) and
(data<>$F8) and
((expected<0) or (data=expected))) do
if (systemtick-t)>=timeout then exit;
Get_data_with_timeout:=true
end;
Procedure Send_Command_to_MPU;
Var
Data:Byte;
t:longint;
Begin
Disable_MPU_Interrupts;
t:=systemtick;
While (Port[MPU_Statport] And MPU_Data_Ready_Mask)<>0 do
if systemtick-t>18 then exit;
Port[MPU_Comport]:=Command;
If (Command=MPU_Reset) and MPU_in_UART_Mode then Mpu_In_Uart_Mode:=false
else
begin
t:=systemtick;
Repeat
if (Port[MPU_Statport] and MPU_Data_Available_Mask)=0 then
begin
data:=port[Mpu_DataPort];
if data<>mpu_ack then
begin
MPU_Indata_buffer[buffer_head]:=data;
buffer_head:=succ(buffer_head) and Buffer_size;
end
end
else data:=0
Until (data=Mpu_ack) or (systemtick-t>18);
end;
If Command=Mpu_Uart_Mode then Mpu_In_Uart_Mode:=true;
Enable_mpu_Interrupts;
end; {Send_Command..}
procedure Send_data_to_mpu(data:byte);
begin
Repeat
Until((Port[MPU_Statport] And MPU_Data_Ready_Mask)=0);
port[mpu_dataport]:=data
end;
procedure clearmpuin;
var b:byte;
begin
while get_data_from_mpu(b) do
end;
{$F+} Procedure Uninstall_MPU401;
Begin
ExitProc:=ExitSave;
Disable_MPU_Interrupts;
SetIntVec(MPU_Interrupt_Number,Old_Int_Vec);
End;
procedure Mpu_Init;
var b,c:byte;
t:longint;
ok:boolean;
begin
Disable_Mpu_Interrupts;
{flush in}
Buffer_Head := Buffer_Start;
Buffer_Tail := Buffer_Start;
b:=port[mpu_dataport];
while ((Port[MPU_Statport] and MPU_Data_Available_Mask)=0) do
b:=port[mpu_dataPort];
ok:=false;
c:=3; {3 tries to reset}
repeat
t:=systemtick;
repeat
ok:=(Port[MPU_Statport] And MPU_Data_Ready_Mask)=0
until ok or (systemtick-t>18);
if not ok then exit;
port[mpu_ComPort]:=mpu_reset;
t:=systemtick;
repeat
ok:=((Port[MPU_Statport] And MPU_Data_Available_Mask)=0) and
(Port[Mpu_DataPort]=Mpu_ack);
until ok or (systemtick-t>18);
Dec(c);
until ok or (c=0);
Mpu_In_Uart_mode:=false;
Enable_mpu_interrupts;
Send_Command_to_mpu(mpu_Uart_mode);
end;
Begin {initialization Code}
ExitSave:=ExitProc;
ExitProc:=@Uninstall_MPU401;
GetIntVec(MPU_Interrupt_Number,Old_Int_Vec);
SetIntVec(MPU_Interrupt_Number,@MPU_Interrupt_Handler);
Mpu_init;
End. {Init Code}